#include "ConnObject.h"
#include "MySQLWorker.h"
#include <vector>

#include <cppconn/driver.h>
#include <cppconn/connection.h>
#include <cppconn/exception.h>
#include <cppconn/statement.h>

// "tcp://127.0.0.1:3306;root;123456"

//str：待拆分的字符串（std::string_view 类型，只读视图）。
//ep：分隔符（单个字符，如逗号 ,、空格 等）。
//eepEmpty：布尔值，控制是否保留空字符串（连续分隔符产生的空值）。
static std::vector<std::string_view>
Tokenize(std::string_view str, char sep, bool keepEmpty)
{
    std::vector<std::string_view> tokens;

    size_t start = 0;
    for (size_t end = str.find(sep); end != std::string_view::npos; end = str.find(sep, start))
    {
        if (keepEmpty || (start < end))
            tokens.push_back(str.substr(start, end - start));
        start = end + 1;
    }

    if (keepEmpty || (start < str.length()))
        tokens.push_back(str.substr(start));

    return tokens;
}


MySQLConnInfo::MySQLConnInfo(const std::string &db,const std::string &url)
{
    auto tokens = Tokenize(url,';',false);
    if (tokens.size() != 3)
    {
        return;
    }
    url_.assign(tokens[0]);
    user_.assign(tokens[1]);
    password_.assign(tokens[2]);
    database_ = db;
}

ConnObject::ConnObject(const std::string &db,const std::string &url,BlockingQueuePro<SqlOperation*> &task_queue)
    : info_(db,url)
{
    workthread_ = new MySQLWorker(task_queue,this);
    workthread_->Start();
}

ConnObject::~ConnObject()
{
    if (workthread_)
    {
        workthread_->Close();
        delete workthread_;
        workthread_ = nullptr;
    }
    if (conn_)
    {
        delete conn_;
    }
}

void ConnObject::Open()
{
    try
    {
        driver_ = get_driver_instance();
        conn_ = driver_->connect(info_.url_,info_.user_,info_.password_);
        conn_->setSchema(info_.database_);
    }
    catch(const sql::SQLException& e)
    {
        HandlerException(e);
    }
}

void ConnObject::Close()
{
    if (conn_)
    {
        conn_->close();
        delete conn_;
        conn_ = nullptr;
    }
}

sql::ResultSet *ConnObject::QueryDatabase(std::string &sql)
{
    try
    {
        sql::Statement *stame = conn_->createStatement();
        return stame->executeQuery(sql);
    }
    catch(const sql::SQLException &e)
    {
        HandlerException(e);
    }
    return nullptr;
}

void ConnObject::HandlerException(const sql::SQLException &e)
{
    if (e.getErrorCode() != 0)
    {
        std::cerr << "# ERR: SQLException in " << __FILE__;
        std::cerr << "(" << __FUNCTION__ << ") on line " << __LINE__ << std::endl;
        std::cerr << "# ERR: " << e.what();
        std::cerr << " (MySQL error code: " << e.getErrorCode();
        std::cerr << ", SQLState: " << e.getSQLState() << " )" << std::endl;
    }
}
